#ifndef LVGLGROUP_H_
#define LVGLGROUP_H_

#include "LvObj.h"
#include "lvglpp.h"

namespace lvglpp
{
	class LvGroup : public lv_group_t
	{
	private:
		LvGroup() = default;

	public:
		using UniquePtr = LvPointer<LvGroup, lv_group_del>;
		static inline UniquePtr Make() noexcept
		{
			static_assert(sizeof(LvGroup) == sizeof(lv_group_t));
			return UniquePtr{(LvGroup *)lv_group_create()};
		}

		static inline LvGroup *create() noexcept
		{
			static_assert(sizeof(LvGroup) == sizeof(lv_group_t));
			return (LvGroup *)lv_group_create();
		}

		/**
		 * Get the default group
		 * @return          pointer to the default group
		 */
		static LvGroup *getDefault(void) noexcept { return (LvGroup *)lv_group_get_default(); }

		/**
		 * Swap 2 object in a group. The object must be in the same group
		 * @param obj1  pointer to an object
		 * @param obj2  pointer to an other object
		 */
		static void swapObj(LvObjRef obj1, LvObjRef obj2) noexcept { return lv_group_swap_obj(obj1.raw(), obj2.raw()); }

		/**
		 * Remove an object from its group
		 * @param obj       pointer to an object to remove
		 */
		static void removeObj(LvObjRef obj) noexcept { return lv_group_remove_obj(obj.raw()); }

		/**
		 * Focus on an object (defocus the current)
		 * @param obj       pointer to an object to focus on
		 */
		static void focusObj(LvObjRef obj) noexcept { return lv_group_focus_obj(obj.raw()); }

	public:
		inline lv_group_t *raw() noexcept { return this; }

		/**
		 * Set a default group. New object are added to this group if it's enabled in their class with `add_to_def_group = true`
		 */
		LvGroup &setDefault() noexcept { return lv_group_set_default(this), *this; }

		/**
		 * Add an object to a group
		 * @param obj       pointer to an object to add
		 */
		LvGroup &addObj(LvObjPtr obj) noexcept { return lv_group_add_obj(this, obj), *this; }

		/**
		 * Remove all objects from a group
		 */
		LvGroup &removeAllObjs() noexcept { return lv_group_remove_all_objs(this), *this; }

		/**
		 * Focus the next object in a group (defocus the current)
		 */
		LvGroup &focusNext() noexcept { return lv_group_focus_next(this), *this; }

		/**
		 * Focus the previous object in a group (defocus the current)
		 */
		LvGroup &focusPrev() noexcept { return lv_group_focus_prev(this), *this; }

		/**
		 * Do not let to change the focus from the current object
		 * @param en        true: freeze, false: release freezing (normal mode)
		 */
		LvGroup &focusFreeze(bool en) noexcept { return lv_group_focus_freeze(this, en), *this; }

		/**
		 * Send a control character to the focuses object of a group
		 * @param c         a character (use LV_KEY_.. to navigate)
		 * @return          result of focused object in group.
		 */
		lv_res_t sendData(uint32_t c) noexcept { return lv_group_send_data(this, c); }

		/**
		 * Set a function for a group which will be called when a new object is focused
		 * @param focus_cb      the call back function or NULL if unused
		 */
		LvGroup &setFocusCb(lv_group_focus_cb_t focus_cb) noexcept { return lv_group_set_focus_cb(this, focus_cb), *this; }

		/**
		 * Set whether the next or previous item in a group is focused if the currently focused obj is
		 * deleted.
		 * @param policy        new refocus policy enum
		 */
		LvGroup &setRefocusPolicy(lv_group_refocus_policy_t policy) noexcept { return lv_group_set_refocus_policy(this, policy), *this; }

		/**
		 * Manually set the current mode (edit or navigate).
		 * @param edit          true: edit mode; false: navigate mode
		 */
		LvGroup &setEditing(bool edit) noexcept { return lv_group_set_editing(this, edit), *this; }

		/**
		 * Set whether focus next/prev will allow wrapping from first->last or last->first object.
		 * @param               en true: wrapping enabled; false: wrapping disabled
		 */
		LvGroup &setWrap(bool en) noexcept { return lv_group_set_wrap(this, en), *this; }

		/**
		 * Get the focused object or NULL if there isn't one
		 * @return              pointer to the focused object
		 */
		LvObjPtr getFocused() const noexcept { return LvObjPtr{lv_group_get_focused(this)}; }

		/**
		 * Get the focus callback function of a group
		 * @return the call back function or NULL if not set
		 */
		lv_group_focus_cb_t getFocusCb() const noexcept { return lv_group_get_focus_cb(this); }

		/**
		 * Get the current mode (edit or navigate).
		 * @return              true: edit mode; false: navigate mode
		 */
		bool getEditing() const noexcept { return lv_group_get_editing(this); }

		/**
		 * Get whether focus next/prev will allow wrapping from first->last or last->first object.
		 * @param               en true: wrapping enabled; false: wrapping disabled
		 */
		bool getWrap() noexcept { return lv_group_get_wrap(this); }

		/**
		 * Get the number of object in the group
		 * @return              number of objects in the group
		 */
		uint32_t getObjCount() noexcept { return lv_group_get_obj_count(this); }
	};
} // namespace lvglpp

#endif // LVGLGROUP_H_
